import { newParseAttrs } from "./utils.js";
import fs from "fs";
import path from "path";

const startTagRegExp = /^<([\w\-]+)([\s\S]*?)>/i;
const endTagRegExp = /^<\/([\w\-\s]+)>/i;
const eleTagExp = /<([\w\-]+)([\s\S]*?)>/i;
// 开始标签验证 记录下idx标识
const ignore = /<!--[\s\S]*?-->/gi; //<!-- ele -->
const ignoreSvg = /<svg[^>]*>[\s\S]*?<\/svg>/gi;
const br = /\n/gi;

/**
 * @param {模板字符串} templateStr
 */

const test = () => {
  const sss = fs.readFileSync(path.resolve(__dirname, "./test.vue"), "utf8");
  parse(sss);
}
// test();

function parse(templateStr, globalConfig, filename) {
  try {
    let idx = 0;
    const tagStack = []; //标签tag栈
    const eleStack = [{ children: [] }]; //ast结构树
    templateStr = templateStr
      .replace(br, "")
      .replace(ignoreSvg, "")
      .replace(ignore, "");

    let rest = templateStr;
    while (idx < templateStr.length - 1) {
      rest = templateStr.substring(idx);
      if (startTagRegExp.test(rest)) {
        const arr = rest.match(startTagRegExp);
        let end = arr[0];
        const tag = arr[1];
        let attrs = arr[2];   
        //不能单纯以startTagRegExp为一个完整的标签如下会导致标签匹配错误
        //<element v-if="a > 1" v-show="a > 10 || a > fn(123)" />
        if (!checkStartTag(end)) {
          let { str } = getCloseTag(rest, end, attrs) || {};
          attrs = attrs + ">" + str?.slice(0, -1);
          end += str;
        }
        idx += end.length;
        // <element />
        const { attrsData, attrsArray, attrsDataObj } = newParseAttrs(
          attrs,
          globalConfig
        );
        // 如下标签无需进栈
        if (
          end.slice(-2).trim() === "/>" ||
          tag.trim() === "img" ||
          tag.trim() === "br" ||
          tag.trim() === "input"
        ) {
          const child = {
            tag: tag,
            children: [],
            attrs: attrs,
            attrsArray: attrsArray,
            attrsData: attrsData,
          };
          // 既不入栈，也不出栈
          if (eleStack.length > 0) {
            eleStack[eleStack.length - 1].children.push(child);
          }
        } else {
          const outerText = rest.substring(end.length);
          tagStack.push(tag);
          eleStack.push({
            tag: tag,
            children: [],
            attrs: attrs,
            attrsArray: attrsArray, //用于处理属性
            attrsData: attrsData,
            outerText: parseOuterText(outerText, tag, filename), //用于处理不被tag包裹的text
          });
        }
        // 记录属性
        if (attrsData?.length > 0) {
          if (!globalConfig.saveReplaceObj[filename]?.attrData) {
            globalConfig.saveReplaceObj[filename].attrData = [
              {
                tag: tag,
                attrsDataObj: attrsDataObj,
              },
            ];
          } else {
            globalConfig.saveReplaceObj[filename].attrData.push({
              tag: tag,
              attrsDataObj: attrsDataObj,
            });
          }
        }
      } else if (endTagRegExp.test(rest)) {
        const tag = rest.match(endTagRegExp)[1];
        //此时tag一定和栈顶相同
        const popTag = tagStack.pop();
        if (tag.trim() === popTag.trim()) {
          const child = eleStack.pop();
          // 检测eleStack是否有children属性
          if (eleStack.length > 0) {
            // child一定是栈顶的子节点
            eleStack[eleStack.length - 1].children.push(child);
          }
        } else {
          throw new Error(`${filename}中： ${popTag}标签1没有闭合${rest}`);
        }
        idx += tag.length + 3;
      } else {
        // 只处理无子节点的情况，如果有子节点，子节点继续递归，无标签的text会在parseText中处理
        //处理<div>aaa</div>
        //不处理<div>aaa<p>bbb</p></div>
        const { hasChild, content, num } = checkHasChildEle(
          rest,
          tagStack[tagStack.length - 1],
          filename
        );
        // eleStack[eleStack.length - 1].children.length === 0 表示确实无任何子节点
        if (!hasChild && eleStack[eleStack.length - 1].children.length === 0) {
          const text = content.trim();
          //证明不是空格
          if (text) {
            eleStack[eleStack.length - 1].children.push({
              innerText: text,
            });
          }
          idx += num;
        } else {
          idx++;
        }
      }
    }
    return eleStack[0].children;
  } catch (e) {
    console.error(e);
  }
}

// 检查标签完整性
function checkStartTag(end) {
  const double = /\"/gi;
  const sign = /\'/gi;
  const num1 = end.match(double);
  const num2 = end.match(sign);
  let bool = true;
  if (num1 && num1.length % 2 !== 0) {
    bool = false;
  }
  if (num2 && num2.length % 2 !== 0) {
    bool = false;
  }
  return bool;
}

/**
 * <div v-if="a>12">
  </div>;
*/
// 如果checkStartTag为false，自动匹配结尾部分
function getCloseTag(str, end, attrStr) {
  let start = end.length;
  let attrs = "";
  while (start < str.length - 1) {
    if (str[start] === ">") {
      const double = /\"/gi;
      const sign = /\'/gi;
      const newstr = attrStr + attrs;
      const num1 = newstr.match(double);
      const num2 = newstr.match(sign);
      const reg = /('|")([\s\S]*?)\1/gi;
      if (
        (!num1 || num1.length % 2 === 0) &&
        (!num2 || num2.length % 2 === 0) &&
        reg.test(newstr)
      ) {
        attrs += str[start];
        return {
          str: attrs,
        };
      }
    }
    attrs += str[start];
    start++;
  }
}

//判断是否有子元素
function checkHasChildEle(str, tag, filename) {
  let right = "";
  let rightIdx = 0;
  // right
  while (rightIdx < str.length - 1) {
    const end = str[rightIdx] + str[rightIdx + 1];
    if (end !== "</") {
      right += str[rightIdx];
      rightIdx++;
    } else {
      break;
    }
  }
  // 有子节点直接return
  if (eleTagExp.test(right)) {
    return {
      hasChild: true,
      content: "",
      num: rightIdx,
    };
  }
  let closeTag = "";
  let idx = rightIdx;
  idx += 2;
  // 匹配到最近的结束标签
  while (true) {
    if (str[idx] !== ">") {
      closeTag += str[idx];
      idx++;
    } else {
      break;
    }
  }
  // 验证闭合标签是否一致
  if (tag.trim() !== closeTag.trim()) {
    throw new Error(`${filename}中： ${tag}与结尾标签不一致`);
  }
  return {
    hasChild: false,
    content: right,
    num: rightIdx,
  };
}

// 处理outerText  aaa<ele>bbb</ele>{{ccc}}</ele> => 提出文本 aaa,ccc
function parseOuterText(outerString, parentTag, filename) {
  outerString = outerString.trim();
  if (!outerString) return [];
  let rest = outerString;
  let idx = 0;
  let content = "";
  let hasChild = false;
  const List = []; //储存文本
  const stack = [parentTag]; //储存tag
  while (idx < outerString.length - 1) {
    rest = outerString.substring(idx);
    if (startTagRegExp.test(rest)) {
      hasChild = true;
      const arr = rest.match(startTagRegExp);
      let end = arr[0];
      const tag = arr[1];
      let attrs = arr[2];

      if (!checkStartTag(end)) {
        let { str } = getCloseTag(rest, end, attrs) || {};
        attrs = attrs + ">" + str?.slice(0, -1);
        end += str;
      }
      idx += end.length;
      if (
        end.slice(-2) !== "/>" &&
        tag.trim() !== "img" &&
        tag.trim() !== "br" && 
        tag.trim() !== "input"
      ) {
        stack.push(tag);
      } else {
        const str = content?.replace(/\&nbsp;/g, '')?.trim();
        if (str) {
          List.push(str);
          content = "";
        }
      }
    } else if (endTagRegExp.test(rest)) {
      const tag = rest.match(endTagRegExp)[1];
      idx += tag.length + 3;
      const popTag = stack.pop();
      // sdfsd (<div>sfsd<span>sdf</span></div>) sdf </div> =>  sdfsd,sdf
      // (<div>sfsd<span>sdf</span></div>)  sdf (<div>sfsdfsd</div>) sdfsd </div>
      if (tag.trim() !== popTag.trim()) {
        throw new Error(`${filename} ： ${popTag} 标签2没有闭合 ${rest}`);
      }
      //aaa</div>
      if (stack.length === 0 && !hasChild) return [];
      //aaa<div>sss</div>bbb</div>
      if (stack.length === 0 && hasChild) {
        const str = content?.replace(/\&nbsp;/g, '')?.trim();
        if (str) {
          List.push(str);
        }
        return List;
      }
      // aa <div>sdf</div>sdf </ele><p>sdfsd</p></div> => sdf
      if (stack.length === 1) {
        const str = content?.replace(/\&nbsp;/g, '')?.trim();
        if (str) {
          List.push(str);
          content = "";
        }
      }
    } else {
      if (stack.length === 1) {
        content += outerString[idx];
      }
      idx += 1;
    }
  }
  return List;
}

export default parse;